<?php
/**
 * Created by IntelliJ IDEA.
 * Author: lemon
 * Time: 2018/09/20 6:04 PM
 * Desc: 微信支付
 */
class WeiXin
{
    private $config;
    private $ssl_cert_path;
    private $ssl_key_path;

    /**
     * Author: lemon
     * Time: 2018/10/25 11:42 AM
     * Desc: 加载文件，设置配置
     * @param $config
     * WeiXin constructor.
     */
    public function __construct($config)
    {
        if (empty($config)){
            $config = [
                'app_id' => 'wxba1e97a69e18d6c1',
                'mch_id' => '1517252691',
                'key' => 'ydyh001ydyhyxgs20181025DOCTORAPP',
                'notify_url' => '/v2/callback/payment/weixin/doctor/notify',
                'api_url' => 'http://app.youdingyihu.com'
            ];
        }
        $this->config = $config;
    }

    /**
     * Author: lemon
     * Time: 2018/10/25 11:47 AM
     * Desc: H5支付
     * @param $order
     * @return mixed
     */
    public function h5($order)
    {
        list($pre_pay_order,$data) = $this->prePayOrder(2 , $order);
        return $pre_pay_order;
    }

    /**
     * Author: lemon
     * Time: 2018/10/25 11:47 AM
     * Desc: APP支付
     * @param $order
     * @return mixed
     */
    public function app($order){
        list($pre_pay_order,$data) = $this->prePayOrder(1 , $order);
        if($pre_pay_order['return_code'] == "SUCCESS" && $pre_pay_order['result_code'] == "SUCCESS"){
            //根据微信支付返回的结果进行二次签名
            //二次签名所需的随机字符串
            $data["nonce_str"] = $this->createNoncestr();
            //二次签名所需的时间戳
            $data['timeStamp'] = time() . "";
            //二次签名剩余参数的补充
            $secondSignArray = array(
                "appid" => $data['appid'],
                "noncestr" => $data['nonce_str'],
                "package" => "Sign=WXPay",
                "prepayid" => $pre_pay_order['prepay_id'],
                "partnerid" => $data['mch_id'],
                "timestamp" => $data['timeStamp'],
            );
            $json['success'] = 1;
            $json['ordersn'] = $data["out_trade_no"]; //订单号
            $json['order_arr'] = $secondSignArray;

            //返给前台APP的预支付订单信息
            $key = $this->config['key'];
            $json['order_arr']['sign'] = $this->getSign($secondSignArray, $key);
            //预支付订单签名
            $json['data'] = "预支付完成";
            //预支付完成,在下方进行自己内部的业务逻辑
            /*****************************/
        }else {
            $json['success'] = 0;
            $json['error'] = $pre_pay_order['return_msg'];
        }

        return $json;
    }

    public function QRCodePay($order){
        list($pre_pay_order,$data) = $this->prePayOrder(2 , $order);
        return $pre_pay_order;
    }

    public function tradeRefund($order){
        $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';

        $key = $this->config['key'];

        $data["appid"] = $this->config['app_id'];
        $data["mch_id"] = $this->config['mch_id'];
        $data["nonce_str"] = $this->createNoncestr();

        //退款信息
        $data['out_trade_no'] = $order['out_trade_no'];
        $data['trade_no'] = $order['trade_no'];
        $data['total_fee'] = $order['total_fee'] * 100;
        $data['refund_amount'] = $order['refund_amount'] * 100;

        //签名
        $data["sign"] = $this->getSign($data, $key);

        $xmlData = $this->arrayToXml($data);

        $result = $this->sendPrePayCurl($url,$xmlData,2);
        $resultCode = $result->return_code;
        if(!empty($resultCode)&& $resultCode == 'SUCCESS'){
            return true;
        } else {
            return false;
        }
    }


    /**
     * Author: lemon
     * Time: 2018/10/29 11:13 AM
     * Desc: 微信验签
     * @param $tmpData
     * @return bool
     */
    public function checkSign($tmpData){
        $key = $this->config['key'];
        $post_sign = $tmpData['sign'];
        unset($tmpData['sign']);
        $sign = $this->getSign($tmpData,$key);//本地签名
        if ($post_sign == $sign) {
            return TRUE;
        }
        return FALSE;
    }

    /**
     * Author: lemon
     * Time: 2018/10/25 11:48 AM
     * Desc: 统一下单
     * @param $type int IOS/Android 1   H5 : 2
     * @param $order
     * @return array
     */
    private function prePayOrder($type, $order){
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $ip= $this->getIp();

        //回调地址
        $notify_url = $this->config['api_url'] . $this->config['notify_url'];
        $body = $order['body'];
        $key = $this->config['key'];

        $data["appid"] = $this->config['app_id'];
        $data["mch_id"] = $this->config['mch_id'];
        $data["nonce_str"] = $this->createNoncestr();
        $data["body"] = $body;
        $data["notify_url"] = $notify_url;

        $data["spbill_create_ip"] = $ip;
        if (1=== $type){
            $data["trade_type"] = "APP";
        }else if(2 === $type){
            $data["trade_type"] = "MWEB";
//            $data["scene_info"] = "{'h5_info': {'type':'Wap','wap_url': $notify_url,'wap_name': $body}}";
            $data['scene_info'] = $order['scene_info'];
        }else{
            $data["trade_type"] = "NATIVE";
        }

        //订单信息
        $data['out_trade_no'] = $order['out_trade_no'];
        $data['total_fee'] = $order['total_amount'] * 100;

        $data['total_fee'] = 1; //测试用1分钱正式环境删除

        //签名
        $data["sign"] = $this->getSign($data, $key);

        $xmlData = $this->arrayToXml($data);

        $get_data = $this->sendPrePayCurl($url,$xmlData);

        return [$get_data,$data];
    }

    function getIp()
    {
        if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
            $ip = getenv("HTTP_CLIENT_IP");
        else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
            $ip = getenv("HTTP_X_FORWARDED_FOR");
        else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
            $ip = getenv("REMOTE_ADDR");
        else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))
            $ip = $_SERVER['REMOTE_ADDR'];
        else
            $ip = "unknown";
        return ($ip);
    }

    /**
     * Author: lemon
     * Time: 2018/10/25 11:49 AM
     * Desc: 将数组转换为xml格式 
     * @param $arr
     * @return string
     */
    public function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * Author: lemon
     * Time: 2018/10/25 11:49 AM
     * Desc: 发送请求
     * @param $url
     * @param $xml
     * @param int $second
     * @param int $type
     * @return bool|mixed
     */
    public function sendPrePayCurl($url, $xml, $type=1, $second = 30){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        if (1 !== $type) {
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLCERT, $this->ssl_cert_path);
            //默认格式为PEM，可以注释
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, $this->ssl_key_path);
        }

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        curl_close($ch);
        $data_xml_arr = $this->XMLDataParse($data);
        if ($data_xml_arr) {
            return $data_xml_arr;
        }else{
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error" . "<br>";
            echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
            curl_close($ch);
            return false;
        }
    }

    /**
     * Author: lemon
     * Time: 2018/10/25 11:50 AM
     * Desc: xml格式数据解析函数
     * @param $data
     * @return mixed
     */
    public function XMLDataParse($data){
        $xml = simplexml_load_string($data, NULL, LIBXML_NOCDATA);
        $array = json_decode(json_encode($xml), true);
        return $array;
    }

    /**
     * Author: lemon
     * Time: 2018/10/25 11:50 AM
     * Desc: 生成随机字符串
     * @param int $length
     * @return string
     */
    function createNoncestr($length = 32){
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++){
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * Author: lemon
     * Time: 2018/10/25 11:50 AM
     * Desc: 格式化参数格式化成url参数  生成签名sign
     * @param $Obj
     * @param $key
     * @return string
     */
    public function getSign($Obj, $key){
        foreach ($Obj as $k => $v) {
            $Parameters[$k] = $v;
        }


        die222;
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //echo '【string1】'.$String.'</br>';
        //签名步骤二：在string后加入KEY
        if ($key) {
            $String = $String . "&key=" . $key;
        }
        //echo "【string2】".$String."</br>";
        //签名步骤三：MD5加密
        $String = md5($String);
        //echo "【string3】 ".$String."</br>";
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        //echo "【result】 ".$result_."</br>";
        return $result_;
    }

    public function getSandboxSignKey($mchId, $nonceStr, $key)
    {
        $url = 'https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey';
        $data['mch_id'] = $mchId;//商户号
        $data['nonce_str'] = $nonceStr;//随机字符串
        $data['sign'] = $this->getSign($data, $key);
        $xml = $this->arrayToXml($data);

        $get_data = $this->sendPrePayCurl($url,$xml);

        if (!empty($get_data) && 'SUCCESS' == $get_data['return_code']){
            return [true, $get_data['sandbox_signkey']];
        }else{
            return [false, $get_data['return_msg']];
        }

    }

    /**
     * Author: lemon
     * Time: 2018/10/25 11:51 AM
     * Desc: 按字典序排序参数
     * @param $paraMap
     * @param $urlencode
     * @return bool|string
     */
    public function formatBizQueryParaMap($paraMap, $urlencode){
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if ($urlencode) {
                $v = urlencode($v);
            }
            //$buff .= strtolower($k) . "=" . $v . "&";
            $buff .= $k . "=" . $v . "&";
        }
        $reqPar = '';
        if (strlen($buff) > 0){
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }

}
